package ru.xeden.web.html.builder;

import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Stack;

import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
import org.xml.sax.ext.LexicalHandler;

import ru.xeden.core.common.Utils;
import ru.xeden.web.html.component.IComponentFactory;
import ru.xeden.web.html.dom.DomMarkUpElement;
import ru.xeden.web.html.dom.Element;
import ru.xeden.web.html.dom.INode;
import ru.xeden.web.html.dom.Node;


public class SaxHandler
    implements LexicalHandler, EntityResolver, ContentHandler
{
    
    private static final String COMPONENT_NODE = "componentId";
    private static final String ID = "id";
    
    private Element currentElement;
    private Stack<Element> stack = new Stack<Element>();
    private StringBuilder stringBuilder = new StringBuilder();
    private Element root;
    private IComponentFactory componentFactory;

    public SaxHandler(IComponentFactory componentFactory)
    {
        this.componentFactory = componentFactory;
    }
    
    public Node getElement()
    {
        return root;
    }

    @Override
    public void setDocumentLocator(Locator locator)
    {
    }

    @Override
    public void startDocument()
        throws SAXException
    {
    }

    @Override
    public void endDocument()
        throws SAXException
    {
    }

    @Override
    public void startPrefixMapping(String prefix, String uri)
        throws SAXException
    {
    }

    @Override
    public void endPrefixMapping(String prefix)
        throws SAXException
    {
    }

    @Override
    public void startElement(String uri, String localName, String qName, Attributes atts)
        throws SAXException
    {
        if (stringBuilder.length() > 0)
        {
            currentElement.text(stringBuilder.toString());
            stringBuilder = new StringBuilder();
        }
        if (root == null)
        {
            root = new DomMarkUpElement(qName);
            currentElement = root;
        }
        else
        {
            INode node = null;
            String componetnIdAttr = getAtrinbuteValueIgnoreCase(COMPONENT_NODE, atts);
            if (!Utils.isEmpty(componetnIdAttr) && componentFactory != null)
            {
                Map<String, String> attsMap = attributesToMap(atts);
                node = componentFactory.createComponent(componetnIdAttr, attsMap);
                if (node != null)
                {
                    currentElement.addChild(node);
                }
            }

            if (node == null)
            {
                DomMarkUpElement e = new DomMarkUpElement(qName);
                setAttributes(e, atts);
                currentElement.addChild(e);
                stack.add(currentElement);
                currentElement = e;
            }
        }
    }

    private void setAttributes(Element element, Attributes atts)
    {
        if (atts != null)
        {
            int length = atts.getLength();
            for (int i = 0; i < length; i++)
            {
                String name = atts.getLocalName(i);
                String value = atts.getValue(i);
                element.attribute(name, value);
            }
        }
    }

    private Map<String, String> attributesToMap(Attributes atts)
    {
        if (atts != null)
        {
            Map<String, String> result = new HashMap<String, String>();
            int length = atts.getLength();
            for (int i = 0; i < length; i++)
            {
                result.put(atts.getLocalName(i), atts.getValue(i));
            }
            return result;
        }
        else
        {
            return Collections.emptyMap();
        }
    }

    private String getAtrinbuteValueIgnoreCase(String attrName, Attributes atts)
    {
        if (atts == null || Utils.isEmpty(attrName))
        {
            return null;
        }
        else
        {
            int length = atts.getLength();
            for (int i = 0; i < length; i++)
            {
                String localName = atts.getLocalName(i);
                if (attrName.equalsIgnoreCase(localName))
                {
                    return atts.getValue(i);
                }
            }
        }
        return null;
    }
    
    @Override
    public void endElement(String uri, String localName, String qName)
        throws SAXException
    {
        if (stringBuilder.length() > 0)
        {
            currentElement.text(stringBuilder.toString());
            stringBuilder = new StringBuilder();
        }
        if (!stack.isEmpty())
        {
            currentElement = stack.pop();
        }
    }

    @Override
    public void characters(char [] ch, int start, int length)
        throws SAXException
    {
        stringBuilder.append(ch, start, length);
    }

    @Override
    public void ignorableWhitespace(char [] ch, int start, int length)
        throws SAXException
    {
    }

    @Override
    public void processingInstruction(String target, String data)
        throws SAXException
    {
    }

    @Override
    public void skippedEntity(String name)
        throws SAXException
    {
    }

    @Override
    public InputSource resolveEntity(String publicId, String systemId)
        throws SAXException, IOException
    {
        return null;
    }

    @Override
    public void startDTD(String name, String publicId, String systemId)
        throws SAXException
    {
    }

    @Override
    public void endDTD()
        throws SAXException
    {
    }

    @Override
    public void startEntity(String name)
        throws SAXException
    {
    }

    @Override
    public void endEntity(String name)
        throws SAXException
    {
    }

    @Override
    public void startCDATA()
        throws SAXException
    {
    }

    @Override
    public void endCDATA()
        throws SAXException
    {
    }

    @Override
    public void comment(char [] ch, int start, int length)
        throws SAXException
    {
    }
}
